package com.datagateway.component;

import com.datagateway.model.ProcessedData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 数据重试队列组件
 * 负责处理失败数据的重试逻辑和死信处理
 * 
 * @author Data Gateway Team
 * @version 1.0.0
 */
@Component
public class RetryQueue {

    private static final Logger logger = LoggerFactory.getLogger(RetryQueue.class);

    @Autowired
    @Qualifier("dataProcessingExecutor")
    private java.util.concurrent.Executor dataProcessingExecutor;

    @Autowired
    private DataBuffer dataBuffer;

    /**
     * 重试队列 - 存储需要重试的数据
     */
    private final BlockingQueue<RetryItem> retryQueue = new LinkedBlockingQueue<>();

    /**
     * 死信队列 - 存储重试次数超限的数据
     */
    private final BlockingQueue<ProcessedData> deadLetterQueue = new LinkedBlockingQueue<>();

    /**
     * 重试统计信息
     */
    private final AtomicLong retryCount = new AtomicLong(0);
    private final AtomicLong successRetryCount = new AtomicLong(0);
    private final AtomicLong deadLetterCount = new AtomicLong(0);

    /**
     * 最大重试次数
     */
    private static final int MAX_RETRY_COUNT = 3;

    /**
     * 重试间隔时间（毫秒）
     */
    private static final long RETRY_INTERVAL_MS = 5000;

    /**
     * 添加失败数据到重试队列
     * 
     * @param data 失败的数据
     * @param errorMessage 错误信息
     */
    public void addToRetryQueue(ProcessedData data, String errorMessage) {
        RetryItem retryItem = new RetryItem(data, errorMessage, 0, System.currentTimeMillis());
        
        try {
            retryQueue.offer(retryItem, 100, TimeUnit.MILLISECONDS);
            logger.info("数据已添加到重试队列: dataId={}, error={}", data.getId(), errorMessage);
        } catch (InterruptedException e) {
            logger.error("添加数据到重试队列时被中断: dataId={}", data.getId(), e);
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 定时处理重试队列
     * 每30秒执行一次
     */
    @Scheduled(fixedDelay = 30000)
    public void processRetryQueue() {
        logger.debug("开始处理重试队列，当前队列大小: {}", retryQueue.size());
        
        while (!retryQueue.isEmpty()) {
            RetryItem retryItem = retryQueue.poll();
            if (retryItem != null) {
                processRetryItem(retryItem);
            }
        }
    }

    /**
     * 处理重试项
     * 
     * @param retryItem 重试项
     */
    @Async("dataProcessingExecutor")
    public void processRetryItem(RetryItem retryItem) {
        try {
            // 检查是否达到最大重试次数
            if (retryItem.getRetryCount() >= MAX_RETRY_COUNT) {
                // 添加到死信队列
                addToDeadLetterQueue(retryItem.getData(), "达到最大重试次数: " + MAX_RETRY_COUNT);
                return;
            }

            // 检查重试间隔时间
            long currentTime = System.currentTimeMillis();
            if (currentTime - retryItem.getLastRetryTime() < RETRY_INTERVAL_MS) {
                // 重新添加到队列，等待下次处理
                retryQueue.offer(retryItem);
                return;
            }

            // 更新重试信息
            retryItem.setRetryCount(retryItem.getRetryCount() + 1);
            retryItem.setLastRetryTime(currentTime);
            retryCount.incrementAndGet();

            logger.info("开始重试处理数据: dataId={}, 重试次数={}", 
                       retryItem.getData().getId(), retryItem.getRetryCount());

            // 尝试重新处理数据
            boolean success = retryProcessData(retryItem.getData());
            
            if (success) {
                successRetryCount.incrementAndGet();
                logger.info("重试处理成功: dataId={}, 重试次数={}", 
                           retryItem.getData().getId(), retryItem.getRetryCount());
            } else {
                // 重试失败，重新添加到队列
                retryQueue.offer(retryItem);
                logger.warn("重试处理失败: dataId={}, 重试次数={}", 
                           retryItem.getData().getId(), retryItem.getRetryCount());
            }

        } catch (Exception e) {
            logger.error("处理重试项时发生异常: dataId={}", retryItem.getData().getId(), e);
            // 异常情况下也重新添加到队列
            retryQueue.offer(retryItem);
        }
    }

    /**
     * 重试处理数据
     * 
     * @param data 要重试的数据
     * @return true如果处理成功
     */
    private boolean retryProcessData(ProcessedData data) {
        try {
            // 重新设置处理状态
            data.setStatus(ProcessedData.ProcessStatus.PROCESSING);
            data.setProcessTime(LocalDateTime.now());
            data.setErrorMessage(null);

            // 重新添加到数据缓冲区
            dataBuffer.addData(data);
            
            // 等待一段时间让处理完成
            Thread.sleep(1000);
            
            return true;
        } catch (Exception e) {
            logger.error("重试处理数据失败: dataId={}", data.getId(), e);
            data.setErrorMessage("重试处理失败: " + e.getMessage());
            return false;
        }
    }

    /**
     * 添加数据到死信队列
     * 
     * @param data 数据
     * @param reason 死信原因
     */
    private void addToDeadLetterQueue(ProcessedData data, String reason) {
        try {
            data.setStatus(ProcessedData.ProcessStatus.FAILED);
            data.setErrorMessage(reason);
            data.setProcessTime(LocalDateTime.now());
            
            deadLetterQueue.offer(data, 100, TimeUnit.MILLISECONDS);
            deadLetterCount.incrementAndGet();
            
            logger.warn("数据已添加到死信队列: dataId={}, 原因={}", data.getId(), reason);
        } catch (InterruptedException e) {
            logger.error("添加数据到死信队列时被中断: dataId={}", data.getId(), e);
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 获取死信队列中的数据
     * 
     * @return 死信队列中的数据列表
     */
    public java.util.List<ProcessedData> getDeadLetterData() {
        java.util.List<ProcessedData> deadLetterData = new java.util.ArrayList<>();
        deadLetterQueue.drainTo(deadLetterData);
        return deadLetterData;
    }

    /**
     * 清空死信队列
     */
    public void clearDeadLetterQueue() {
        deadLetterQueue.clear();
        logger.info("死信队列已清空");
    }

    /**
     * 获取重试统计信息
     * 
     * @return 重试统计信息
     */
    public RetryStatistics getRetryStatistics() {
        return new RetryStatistics(
            retryQueue.size(),
            deadLetterQueue.size(),
            retryCount.get(),
            successRetryCount.get(),
            deadLetterCount.get()
        );
    }

    /**
     * 重试项内部类
     */
    public static class RetryItem {
        private final ProcessedData data;
        private final String originalErrorMessage;
        private int retryCount;
        private long lastRetryTime;

        public RetryItem(ProcessedData data, String originalErrorMessage, int retryCount, long lastRetryTime) {
            this.data = data;
            this.originalErrorMessage = originalErrorMessage;
            this.retryCount = retryCount;
            this.lastRetryTime = lastRetryTime;
        }

        // Getter和Setter方法
        public ProcessedData getData() { return data; }
        public String getOriginalErrorMessage() { return originalErrorMessage; }
        public int getRetryCount() { return retryCount; }
        public void setRetryCount(int retryCount) { this.retryCount = retryCount; }
        public long getLastRetryTime() { return lastRetryTime; }
        public void setLastRetryTime(long lastRetryTime) { this.lastRetryTime = lastRetryTime; }
    }

    /**
     * 重试统计信息类
     */
    public static class RetryStatistics {
        private final int retryQueueSize;
        private final int deadLetterQueueSize;
        private final long totalRetryCount;
        private final long successRetryCount;
        private final long deadLetterCount;

        public RetryStatistics(int retryQueueSize, int deadLetterQueueSize, long totalRetryCount, 
                              long successRetryCount, long deadLetterCount) {
            this.retryQueueSize = retryQueueSize;
            this.deadLetterQueueSize = deadLetterQueueSize;
            this.totalRetryCount = totalRetryCount;
            this.successRetryCount = successRetryCount;
            this.deadLetterCount = deadLetterCount;
        }

        // Getter方法
        public int getRetryQueueSize() { return retryQueueSize; }
        public int getDeadLetterQueueSize() { return deadLetterQueueSize; }
        public long getTotalRetryCount() { return totalRetryCount; }
        public long getSuccessRetryCount() { return successRetryCount; }
        public long getDeadLetterCount() { return deadLetterCount; }
        public double getRetrySuccessRate() { 
            return totalRetryCount > 0 ? (double) successRetryCount / totalRetryCount * 100 : 0; 
        }

        @Override
        public String toString() {
            return String.format("RetryStatistics{retryQueueSize=%d, deadLetterQueueSize=%d, " +
                               "totalRetryCount=%d, successRetryCount=%d, deadLetterCount=%d, " +
                               "retrySuccessRate=%.2f%%}",
                               retryQueueSize, deadLetterQueueSize, totalRetryCount, 
                               successRetryCount, deadLetterCount, getRetrySuccessRate());
        }
    }
}
